import { cloneDeep } from 'lodash-es';

/**
 * 数组转 Option
 * @param dict 字典
 * @param label
 * @param value
 * @returns
 */
export const listToOption = (
  list: any[] = [],
  label?: string,
  value?: string,
  dataType?: 'string' | 'int',
): Global.Option[] => {
  return list.map((v: any) => {
    let _value = v[value || 'value'];
    if (dataType === 'string') {
      _value = _value.toString();
    } else if (dataType === 'int') {
      _value = parseFloat(_value);
    }
    return {
      label: v[label || 'label'],
      value: _value,
    };
  });
};

/**
 * 数组转字典
 */
export const listToEnum = (
  list: any[] = [],
  value?: string,
  label?: string,
) => {
  return list.reduce<Record<string, any>>((prev, cur) => {
    prev[cur[value || 'value'] as string] = cur[label || 'label'];
    return prev;
  }, {});
};

/**
 * 格式化树
 * @param tree
 * @param props
 * @returns
 */

export const formatTree = (
  tree: any,
  props?: {
    labelKey?: string;
    valueKey?: string;
    childrenKey?: string;
    label?: string;
    value?: string;
    children?: string;
  },
) => {
  const label = props?.label || 'label';
  const value = props?.value || 'value';
  const children = props?.children || 'children';
  const labelKey = props?.labelKey || 'label';
  const valueKey = props?.valueKey || 'value';
  const childrenKey = props?.childrenKey || 'children';

  // 栈初始化为输入树的根节点
  const stack = [...tree];
  // 初始化一个结果数组，用于存储格式化后的节点
  const result = [];

  // 用于记录父节点的映射关系
  const nodeMap = new Map();

  while (stack.length > 0) {
    const node = stack.shift(); //pop(); // 取出栈顶的节点

    // 创建格式化的节点
    const formattedNode = {
      ...node,
      [labelKey]: node[label],
      [valueKey]: node[value],
      [childrenKey]: [],
    };

    // 将当前节点存入映射中
    nodeMap.set(node, formattedNode);

    // 查找当前节点的父节点（如果有的话）
    const parentNode = nodeMap.get(node.parent);
    if (parentNode) {
      parentNode.children.push(formattedNode);
    } else {
      result.push(formattedNode);
    }

    // 如果当前节点有子节点，将它们压入栈中
    if (node[children]) {
      node[children].forEach((child: any) => {
        // 在压入栈之前，设置每个子节点的父节点引用
        child.parent = node;
        stack.push(child);
      });
    }
  }

  return result;
};

/**
 * 树扁平化
 */
export const treeToArray = (
  tree: any[],
  props?: {
    label?: string;
    value?: string;
    children?: string;
  },
) => {
  const label = props?.label || 'label';
  const value = props?.value || 'value';
  const children = props?.children || 'children';
  const stack = [...tree];
  const result = [];

  while (stack.length > 0) {
    const node = stack.pop();
    result.push({ ...node, value: node[value], label: node[label] });

    if (node[children]) {
      for (let i = node[children].length - 1; i >= 0; i--) {
        stack.push({
          ...node[children][i],
          __parentId: node[value],
        });
      }
    }
  }
  return result;
};

/**
 * 树转Enum
 */
export const treeToEnum = (
  tree: any[],
  props?: {
    label?: string;
    value?: string;
    children?: string;
  },
) => {
  const label = props?.label || 'label';
  const value = props?.value || 'value';
  const children = props?.children || 'children';
  const stack = [...tree];
  const result: Record<string, any> = {};

  while (stack.length > 0) {
    const node = stack.pop();
    result[node[value]] = node[label];

    if (node[children]) {
      for (let i = node[children].length - 1; i >= 0; i--) {
        stack.push(node[children][i]);
      }
    }
  }
  return result;
};

/**
 * 构造树型结构数据
 * @param {*} data 数据源
 * @param {*} id id字段 默认 'id'
 * @param {*} parentId 父节点字段 默认 'parentId'
 * @param {*} children 孩子节点字段 默认 'children'
 */
export function handleTree(
  data: any,
  id?: string,
  parentId?: string,
  children?: string,
) {
  const config = {
    id: id || 'id',
    parentId: parentId || 'parentId',
    childrenList: children || 'children',
  } as Record<string, any>;

  const childrenListMap: Record<string, any> = {};
  const nodeIds: Record<string, any> = {};
  const tree: Record<string, any>[] = [];

  for (const d of data) {
    const parentId = d[config.parentId];
    if (childrenListMap[parentId] == null) {
      childrenListMap[parentId] = [];
    }
    nodeIds[d[config.id]] = d;
    childrenListMap[parentId].push(d);
  }

  for (const d of data) {
    const parentId = d[config.parentId];
    if (nodeIds[parentId] == null) {
      tree.push(d);
    }
  }

  for (const t of tree) {
    adaptToChildrenList(t);
  }

  function adaptToChildrenList(o) {
    if (childrenListMap[o[config.id]] !== null) {
      o[config.childrenList] = childrenListMap[o[config.id]];
    }
    if (o[config.childrenList]) {
      for (const c of o[config.childrenList]) {
        adaptToChildrenList(c);
      }
    }
  }
  return tree;
}

/**
 * noop
 */
export function noop(): void {}

/**
 * debounce
 */
export const debounce = (fn: Function, wait: number) => {
  let int: number;
  return function (...args: any[]) {
    clearInterval(int);
    int = setTimeout(() => {
      fn.apply(this, args);
    }, wait);
  };
};

/**
 * 下载 blob 文件
 * @param blob
 * @param filename
 */
export const download = (blob: Blob, filename: string) => {
  const _blob = new Blob([blob], { type: 'text/plain' });
  const url = URL.createObjectURL(_blob);
  const a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  a.download = filename.replace(new RegExp('"', 'g'), '');
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
};

/**
 * enum to options
 */
export const enumToOption = (e: Record<string, any>): Global.Option[] => {
  return Object.keys(e).map((key) => {
    return {
      label: e[key],
      value: key,
    };
  });
};

/**
 *  hasOwnProperty 判断对象中是否存在某个 key
 */
export const hasOwnProperty = (target: Record<string, any>, key: string) => {
  return Object.prototype.hasOwnProperty.call(target, key);
};

/**
 * 获取树型数据展开第一层级对应 id
 */
export const getTreeFirstChildrenValue = (treeData: any[], value = 'value') => {
  const result: any[] = [];
  const firstData = treeData[0];
  if (firstData) {
    result.push(firstData[value]);
    if (firstData.children?.length) {
      {
        result.push(...getTreeFirstChildrenValue(firstData.children, value));
      }
    }
  }
  return result;
};

export function normalizeTreeData(
  nodes: any[],
  props = {
    label: 'label',
    value: 'value',
    children: 'children',
  },
) {
  const _label = props.label || 'label';
  const _value = props.value || 'value';
  const _children = props.children || 'children';
  const result: any[] = [];
  const queue = nodes.map((node) => ({
    node,
    parent: null,
    parentNormalizedChildren: result,
  })); // 初始化队列

  while (queue.length) {
    const { node, parent, parentNormalizedChildren } = queue.shift(); // 取出当前节点

    // 标准化当前节点
    const normalizedNode = {
      checkable: true,
      disableCheckbox: false,
      disabled: false,
      isLeaf: !!node[_children]?.length || false,
      key: node[_value],
      selectable: true,
      title: node[_label],
      value: node[_value],
      children: [],
    };

    // 将标准化后的节点添加到其父节点的 children 或顶层 result
    parentNormalizedChildren.push(normalizedNode);

    // 如果有子节点，将子节点及其父级关系加入队列
    if (node.children && node.children.length) {
      normalizedNode.isLeaf = false;
      queue.push(
        ...node.children.map((child) => ({
          node: child,
          parent: node,
          parentNormalizedChildren: normalizedNode.children,
        })),
      );
    } else {
      normalizedNode.isLeaf = true;
      normalizedNode.children = undefined; // 没有子节点时删除 children 字段
    }
  }
  return result;
}

/**
 * 获取 location.search 中的参数
 * @returns
 */
export function getQueryParams() {
  const searchParams = new URLSearchParams(window.location.search);
  const params: Record<string, string> = {};

  // 遍历所有查询参数并存储到对象中
  searchParams.forEach((value, key) => {
    params[key] = value;
  });

  return params;
}

/**
 * 判断 Object 是否为空
 */
export function isEmptyObject(record: Record<string, any>) {
  return !Object.keys(record).length;
}

/**
 * 日期格式化
 */
export function parseTime(time: any, pattern?: any) {
  if (arguments.length === 0 || !time) {
    return null;
  }
  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
  let date;
  if (typeof time === 'object') {
    date = time;
  } else {
    if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
      time = parseInt(time);
    } else if (typeof time === 'string') {
      time = time
        .replace(new RegExp(/-/gm), '/')
        .replace('T', ' ')
        .replace(new RegExp(/\.[\d]{3}/gm), '');
    }
    if (typeof time === 'number' && time.toString().length === 10) {
      time = time * 1000;
    }
    date = new Date(time);
  }
  const formatObj: any = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay(),
  };
  const time_str = format.replace(
    /{(y|m|d|h|i|s|a)+}/g,
    (result: any, key: any) => {
      let value = formatObj[key];
      // Note: getDay() returns 0 on Sunday
      if (key === 'a') {
        return ['日', '一', '二', '三', '四', '五', '六'][value];
      }
      if (result.length > 0 && value < 10) {
        value = '0' + value;
      }
      return value || 0;
    },
  );
  return time_str;
}

export function addDisableProperty(data: any) {
  data.forEach((item: any) => {
    if (item.type === '1' && item.id != '0') {
      item.disabled = true;
      if (item.children) {
        const hasType2Child = item.children.some(
          (child: any) => child.type === '2',
        );
        if (hasType2Child) {
          item.disabled = false;
        }
      }
    } else if (item.type === '2') {
      item.disabled = false;
    }
    if (item.children) {
      addDisableProperty(item.children);
    }
  });
  return data;
}

/**
 * el-table-column 自适应列宽
 * @param label: 表头
 * @param tableData 表格数据
 * @param prop: 表格属性
 * @param num 列中有标签等加的富余量
 */
export function columnWidth(
  prop: string,
  tableData: Record<string, any>[],
  label: string,
  num = 0,
): string {
  // 1.获取该列的所有数据并包含表头
  const arr = [...tableData.map((x) => x[prop]), label];

  // 2.计算最大宽度
  const textWidth = arr.reduce((maxWidth, item) => {
    if (!item) return maxWidth;

    // 创建临时span计算宽度
    const span = document.createElement('span');
    span.style.fontSize = '14px';
    span.innerText = String(item);
    span.className = 'getTextWidth';

    const body = document.querySelector('body');
    if (!body) return maxWidth;

    body.appendChild(span);
    const width = span.offsetWidth;
    span.remove();

    return Math.max(maxWidth, width);
  }, 0);

  // 3.返回最终宽度(加上内边距和额外宽度)
  return `${textWidth + 25 + num}px`;
}

// Utf8Array转字符串
export const Utf8ArrayToStr = (array: any) => {
  var out, i, len, c;
  var char2, char3;
  out = '';
  len = array.length;
  i = 0;
  while (i < len) {
    c = array[i++];
    switch (c >> 4) {
      case 0:
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
        // 0xxxxxxx
        out += String.fromCharCode(c);
        break;
      case 12:
      case 13:
        // 110x xxxx 10xx xxxx
        char2 = array[i++];
        out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
        break;
      case 14:
        // 1110 xxxx 10xx xxxx 10xx xxxx
        char2 = array[i++];
        char3 = array[i++];
        out += String.fromCharCode(
          ((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0),
        );
        break;
    }
  }

  return out;
};

export function addDisableProperty2(list: any) {
  const data = cloneDeep(list);
  data.forEach((item: any) => {
    if (item.type === '1') {
      item.disabled = true;
    } else if (item.type === '2') {
      item.disabled = false;
    }
    if (item.children) {
      addDisableProperty2(item.children);
    }
  });
  return data;
}

export const filterTreeByName = (tree: any, searchString: any) => {
  const newTree = cloneDeep(tree);
  const filterNode = (node: any) => {
    if (node.name.includes(searchString)) {
      const newNode = { ...node };

      if (newNode.children && newNode.children.length > 0) {
        newNode.children = newNode.children
          .map(filterNode)
          .filter((child: any) => child !== null);
      }

      return newNode;
    } else {
      if (node.children && node.children.length > 0) {
        const filteredChildren = node.children
          .map(filterNode)
          .filter((child: any) => child !== null);

        if (filteredChildren.length > 0) {
          return { ...node, children: filteredChildren };
        }
      }

      return null;
    }
  };

  return newTree.map(filterNode).filter((node: any) => node !== null);
};

export const findCodeInTree = (tree: any, field: any, value: any) => {
  const traverse: any = (node: any) => {
    if (node && typeof node === 'object') {
      if (node.hasOwnProperty(field) && node[field] === value) {
        return node.thisCode;
      }

      if (node.children && Array.isArray(node.children)) {
        for (let child of node.children) {
          const thisCode = traverse(child);
          if (thisCode !== null) {
            return thisCode;
          }
        }
      }
    }

    return null;
  };

  for (let rootNode of tree) {
    const thisCode = traverse(rootNode);
    if (thisCode !== null) {
      return thisCode;
    }
  }
  return null;
};
